﻿
using System;
using System.Net;
using System.Diagnostics;
using System.Threading;
using System.IO.Ports;
using System.Xml;
using System.Xml.XPath;
using System.Runtime.InteropServices;
using System.Collections.Generic;

using Communicator;
using ParametersParser;
using ArduinoStepper;
using Indicators;
using System.Xml.Linq;


namespace IndicatorControl
{
    using System.Timers;
    /// <summary>
    /// Application modes enumeration
    /// </summary>
    public enum ApplicationMode { AUTO, MENU };

    class Program
    {
       
        internal static SourceSwitch traceSwitch = new SourceSwitch("Appl") { Level = SourceLevels.Information };
        internal static TraceSource trace = new TraceSource("Appl") { Switch = traceSwitch };
        //------------------------------------------------------------------------------
        static void Main(string[] args)
        {
  
            ApplicationMode AppMode;
            // Mode selection by the command line key
            /*if (args.Length == 0 || args[0] == "-auto")
                AppMode = ApplicationMode.AUTO;
            else */
                AppMode = ApplicationMode.MENU;
                trace.TraceInformation("Application has started");
                Dispetcher Disp = new Dispetcher(AppMode);
        }
    }

    /// <summary>
    /// Dispetcher of indicators
    /// </summary>
    public class Dispetcher
    {
        private ApplicationMode AppMode;
        /// <summary>
        /// List of the serial ports
        /// </summary>
        public List<string> SerialPortNameList = new List<string>();
        /// <summary>
        /// List of the motor controllers
        /// </summary>
        public List<Controller> Controllers;
        /// <summary>
        /// List of the indicators
        /// </summary>
        public List<Indicator> Indicators;
        public Communicator.Communicator Communicator;
        /// <summary>
        /// Flag of the indicators tracing.
        /// </summary>
        public Boolean IndicatorsTracing = true;

        //public XDocument IndicatorsValuesLog;

        /// <summary>
        /// Dispetcher constructor
        /// </summary>
        /// <param name="AppMode">Automatic or menu mode of application</param>
        public Dispetcher(ApplicationMode AppMode)
        {
            this.AppMode = AppMode;
            // Содержит конфигурационные настройки программы, кроме сетевых.
            XmlDocument doc = new XmlDocument();
            doc.Load("config.xml");

            XmlNode root = doc.DocumentElement;

            XmlNodeList NodeList;
            string str;

            Communicator = new Communicator.Communicator();

            // Initialization of motor controllers
            // Выходим на раздел контроллеров в конфигураторе
            NodeList = root.SelectNodes("descendant::Controllers/Controller");
            // Создаем новый пустой список контроллеров
            Controllers = new List<Controller>();
            byte motorNumber;

            foreach (XmlNode Node in NodeList)
            {
                // Считываем имя порта, к которому подключен контроллер и адрес контроллера
                str = Node.Attributes.GetNamedItem("port").InnerText;
                // Создаем контроллер и добавляем его в список.
                Controller Controller = new Controller(str);
                Controllers.Add(Controller);
                // Обходим параметры контроллера в конфигураторе и заносим в контроллер
                foreach (XmlNode ChildNode in Node.ChildNodes)
                {
                    str = Convert.ToString(ChildNode.Attributes.GetNamedItem("number").InnerText);
                    //Номер двигателя
                    motorNumber = Convert.ToByte(str);
                    // Угол поворота одного шага двигателя
                    str = Convert.ToString(ChildNode.Attributes.GetNamedItem("anglePerStep").InnerText);
                    Controller.Motors[motorNumber].AnglePerStep = Convert.ToSingle(str);
                }
            }

            #region Initialization of the indicators
            // Выходим на раздел индикаторов в конфигураторе
            NodeList = root.SelectNodes("descendant::Indicators/Indicator");
            // Созадем пустой список индикаторов
            Indicators = new List<Indicator>();
            IndicatorTypes IndType;
            byte MotorNumber;
            string indicatorName = "";
            // Обходим конфигураторы индикаторов и создаем индикаторы
            foreach (XmlNode Node in NodeList)
            {
                // Имя индикатора
                indicatorName = Node.Attributes.GetNamedItem("name").InnerText;
                // Номер контроллера
                byte controllerNumber = Convert.ToByte(Node.Attributes.GetNamedItem("controller").InnerText);
                // Двигатель индикатора
                MotorNumber = Convert.ToByte(Node.Attributes.GetNamedItem("motor").InnerText);
                str = Convert.ToString(Node.Attributes.GetNamedItem("type").InnerText);
                // Тип индикатора
                IndType = (IndicatorTypes)Enum.Parse(typeof(IndicatorTypes), str);
                // Создаем индикатор соответствующего типа и заносим в список
                Indicator indicator;
                if (IndType == IndicatorTypes.Dial)
                    indicator = new DialIndicator(indicatorName, Controllers[controllerNumber], MotorNumber, this.Communicator);
                else if (IndType == IndicatorTypes.Multirotor)
                    indicator = new MultiRotorIndicator(indicatorName, Controllers[controllerNumber], MotorNumber, this.Communicator);
                else
                    indicator = null;

                Indicators.Add(indicator);
                // Настраиваем индикатор значениями из конфигуратора
                indicator.AngleFactor = Convert.ToSingle(Node.Attributes.GetNamedItem("angleFactor").InnerText);
                // Network configuration settings
                indicator.ParName = Node.Attributes.GetNamedItem("parName").InnerText;
                // Init position settings
                indicator.MaxSearchAngle = Convert.ToSingle(Node.FirstChild.Attributes.GetNamedItem("maxSearchAngle").InnerText);
                indicator.SensorAngle = Convert.ToSingle(Node.FirstChild.Attributes.GetNamedItem("sensorAngle").InnerText);
                indicator.InitialSearchShift = Convert.ToSingle(Node.FirstChild.Attributes.GetNamedItem("initialSearchShift").InnerText);
            }
            #endregion

            # region Application start
            if (AppMode == ApplicationMode.MENU)
            {
                string key;
                Boolean Exit = false;
                do
                {
                    // Choosing the action
                    Console.WriteLine("1. Start listening the network");
                    Console.WriteLine("q. Quit");
                    key = Console.ReadLine();
                    switch (key[0])
                    {
                        case '1':
                            {
                                fly();
                                break;
                            }
                        case 'q':
                            {
                                Exit = true;
                                break;
                            };
                        default:
                            break;
                    }
                } while (Exit == false);
            }
            else
                fly();
            #endregion
        }

        static void Program_UnhandledException(object sender, UnhandledExceptionEventArgs e)
        {
            Console.WriteLine(((Exception)e.ExceptionObject).Message);
        }

        #region Implementation

        /// <summary>
        /// Indicator tracing procedure
        /// </summary>
        private void fly()
        {

            IndicatorsTracing = true;
            foreach (Indicator indicator in Indicators)
                indicator.Start();

            // This thread checks the user key pressing
            while (true)
            {
                ConsoleKeyInfo cki = new ConsoleKeyInfo();
                // fly stopping using Q key
                if (Console.KeyAvailable == true)
                {
                    cki = Console.ReadKey(true);
                    if (cki.Key == ConsoleKey.Q)
                    {
                        IndicatorsTracing = false;
                        foreach (Indicator indicator in Indicators)
                            indicator.Stop();
                        break;
                    }
                }
                Console.Clear();
                Console.WriteLine("Индикатор \t Состояние \t Пар модели \t Цел полож \t Подтв полож");
                foreach (Indicator indicator in Indicators)
                {
                    Console.WriteLine("  {0} \t {1} \t {2} \t {3} \t {4}", indicator.Name, indicator.State, indicator.ModelValue, indicator.TargetIndicatorValue, indicator.LastStopedIndicatorValue);
                }
                Thread.Sleep(500);
            }
        #endregion
        }
    }

}
